#include <iostream>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <string.h>
#include <errno.h>

#define SERV_PORT 9877  //default server port 
#define LISTENQ 100     //listen backlog

void transferData(int sockfd,int arg);//transfer data from server to client
void sig_chld(int signo);//deal with the SIGCHLD signal
int argumenthandler(int argc,char **argv);//deal with the arguments

static int BUFFERSIZE = 1024;//default buffer size = 1KB
static bool QUIET = false;//print the transfer info defaultly
using namespace std;

int main(int argc,char ** argv){

	int sockfd,connfd; //listen and connection socket descriptors
	struct sockaddr_in servaddr;// server's socket address 
	char buff[BUFFERSIZE+1];//send or receive buffer

	if(argumenthandler(argc,argv))//deal with the command line arguments
		return -1;

	signal(SIGCHLD,sig_chld);//catch the SIGCHLD signal

	//create a listening socket
	if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0){
		perror("socket error");
			exit(-1);
	}

    //set SO_REUSEADDR socket option
    const int on = 1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
    {
        perror("setsockopt error");
        exit(-1);
    }

    //fill in the socket address structure
	bzero(&servaddr,sizeof(struct sockaddr_in));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	//bind to the listening socket
	if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in))<0){
 		perror("bind error");
		exit(-1);
	}

    //listen on the default port
	if(listen(sockfd,LISTENQ)<0){
		perror("listen error");
		exit(-1);
	}

    //deal with the comming connections from clients
    int number = 0;//specifies the reading file
	pid_t pid;//process #id
	for(;;){

		//accept connection and create new connection socket descriptor
		if((connfd = accept(sockfd,NULL,NULL))<0){
			if(errno==EINTR)
                continue;
            else{ 
                perror("accept error");
			    exit(-1);
            }
		}
		
        //let subprocess deal with communication											
		if((pid = fork())==0){
		    close(sockfd);//close the listening socket

        	//check whether the request is right
			ssize_t n;
			if((n = recv(connfd,buff,BUFFERSIZE,0))<0){
				perror("recv error");
				exit(-1);
			}
			buff[n] = '\0';
			if(strcmp(buff,"START")){
				perror("wrong request");
				exit(-1);
			}
			cout<<"Client "<<number<<" connects to "<<sockfd<<" on "<<connfd<<endl;
			//start transfering data
			transferData(connfd,number);
			close(connfd);//close the connection socket
			exit(0);
		}
		close(connfd);//close the connection socket
		++number;
        
	}
	exit(0);
}

void transferData(int sockfd,int arg){
	stringstream filename;
    ifstream inFile;

	char buff[BUFFERSIZE+1];
	int num = arg%3+2;
	//get the file name to be read from
	filename.str("");
	filename<<"/disk"<<num<<"/file/_co"<<(arg%60);
    cout<<"Open file"<<filename.str()<<endl;
    inFile.open( ( filename.str() ).c_str(), ifstream::binary);
    if (!inFile.is_open()) {
        cout << "Cannot open file:" << filename << endl;
        exit(-1);
    }
    
    ssize_t n;
	while(!inFile.eof()){
        inFile.read(buff,BUFFERSIZE);
        if((n = inFile.gcount())<=0)
            break;
		if(send(sockfd,buff,n,0)<0){
			perror("send error");
			exit(-1);
		}
		if(!QUIET)
			cout<<"send "<<n<<" bytes to client "<<arg<<endl;
	}

	cout<<"send file "<<filename.str()<<" finishes"<<endl;
    inFile.close();
}

void printUsage(){
	cout<<"Usage: ./server [-b buffersize] [-q] [-h]"<<endl
		<<"\t-b set the buffersize for every read or write"<<endl
		<<"\t-q disable to print the information"<<endl
		<<"\t-h for help information"<<endl;
}

int argumenthandler(int argc,char **argv){
	int ch;
	while((ch  = getopt(argc,argv,"b:hq"))!=-1){
		switch(ch){
			case 'b'://set buffersize
				BUFFERSIZE = atoi(optarg);
				break;
			case 'q'://disable to print 
				QUIET = true;
				break;
			case 'h'://help  info
				printUsage();
				return -1;
				break;
			default:
				cout<<"Unsupported arguments: "<<(char)ch<<endl;
				printUsage();
				return -1;
		}
	}
	return 0;
}

void sig_chld(int signo){
	pid_t pid;
	while((pid=waitpid(-1,NULL,0))>0);
}
